#include "FanNum.h"

#include <string.h>
#include <fan-llvm/core/Core.h>
#include "Decimal.h"
#include "FanFloat.h"
#include "FanInt.h"
#include "Type.h"
#include "UnsupportedErr.h"


Type* Num_classType = NULL;
VTable* Num_classVTable = NULL;


/**********************************************
 *
 * Methods and fields
 *
 **********************************************/


/* Int toInt() */
Int Num_toInt(Num self)
{
  Type* type = Obj_typeof(self);

  if (type == Float_classType)
    return (Int)VAL_GET_FLOAT(self);
  else if (type == Int_classType)
    return VAL_GET_INT(self);
  else if (type == Decimal_classType)
  {
    Decimal* d = VAL_GET_REF(self);
    return (Int)d->val;
  }

  return 0;
}


/* Float toFloat() */
Float Num_toFloat(Num self)
{
  Type* type = Obj_typeof(self);

  if (type == Int_classType)
    return (Float)VAL_GET_INT(self);
  else if (type == Float_classType)
    return VAL_GET_FLOAT(self);
  else if (type == Decimal_classType)
  {
    Decimal* d = VAL_GET_REF(self);
    return (Float)d->val;
  }

  return 0.0;
}


/* Decimal toDecimal() */
Decimal* Num_toDecimal(Num self)
{
  Type* type = Obj_typeof(self);

  if (type == Int_classType)
    return Decimal_new(VAL_GET_INT(self));
  else if (type == Float_classType)
    return Decimal_new(VAL_GET_FLOAT(self));
  else if (type == Decimal_classType)
    return VAL_GET_REF(self);

  return NULL;
}


/* Str localeDecimal() */
Str* Num_localeDecimal(void)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Num.localeDecimal not implemented yet"));
  return NULL;
}


/* Str localeGrouping() */
Str* Num_localeGrouping(void)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Num.localeGrouping not implemented yet"));
  return NULL;
}


/* Str localeMinus() */
Str* Num_localeMinus(void)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Num.localeMinus not implemented yet"));
  return NULL;
}


/* Str localePercent() */
Str* Num_localePercent(void)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Num.localePercent not implemented yet"));
  return NULL;
}


/* Str localePosInf() */
Str* Num_localePosInf(void)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Num.localePosInf not implemented yet"));
  return NULL;
}


/* Str localeNegInf() */
Str* Num_localeNegInf(void)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Num.localeNegInf not implemented yet"));
  return NULL;
}


/* Str localeNaN() */
Str* Num_localeNaN(void)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Num.localeNaN not implemented yet"));
  return NULL;
}


/**********************************************
 *
 * Type-system functions
 *
 **********************************************/


Type* Num_initType(void)
{
  if (Num_classType == NULL)
  {
    Type* baseType = Obj_initType();
    VTable* vtable = Num_initVTable();
    Type* type = Type_new("Num", baseType, vtable, sizeof(FanNum), sizeof(FanNumVTable));
    Type_registerMethodImplementation(type, "localeDecimal", Num_localeDecimal, 0);
    Type_registerMethodImplementation(type, "localeGrouping", Num_localeGrouping, 0);
    Type_registerMethodImplementation(type, "localeMinus", Num_localeMinus, 0);
    Type_registerMethodImplementation(type, "localeNaN", Num_localeNaN, 0);
    Type_registerMethodImplementation(type, "localeNegInf", Num_localeNegInf, 0);
    Type_registerMethodImplementation(type, "localePercent", Num_localePercent, 0);
    Type_registerMethodImplementation(type, "localePosInf", Num_localePosInf, 0);
    Type_registerMethodImplementation(type, "toDecimal", Num_toDecimal, 0);
    Type_registerMethodImplementation(type, "toFloat", Num_toFloat, 0);
    Type_registerMethodImplementation(type, "toInt", Num_toInt, 0);
    Num_classType = type;
    Num_initStatic();
  }
  return Num_classType;
}


VTable* Num_initVTable(void)
{
  if (Num_classVTable == NULL)
  {
    VTable* vtable = memoryAlloc(sizeof(FanNumVTable));
    memcpy(vtable, Obj_initVTable(), sizeof(FanObjVTable));
    Num_classVTable = vtable;
  }
  return Num_classVTable;
}


void Num_initStatic(void)
{
}
